package com.ecr.hub.service.reporting.impl;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.stereotype.Service;

import com.ecr.hub.ApplicationException;
import com.ecr.hub.DaoApplicationException;
import com.ecr.hub.database.dao.IDaoReporting;
import com.ecr.hub.model.HubReportData;
import com.ecr.hub.model.enumeration.HubSillonType;
import com.ecr.hub.model.user.User;
import com.ecr.hub.service.reporting.IReportingManager;
import com.ecr.hub.util.LabelUtils;
import com.ecr.hub.util.ManagedBeanUtils;

/**
 * Les statistiques sont écrites très rapidement, à voir plus tard pour
 * optimiser et rationnaliser le code.
 */
@Service("reportManager")
public class ReportingManagerImpl implements IReportingManager {

	// Le séparateur htm.
	private static final String PATH_HTML_SEPARATOR = "/";
	// Le spéparateur pour le fichier csv.
	private static final String CSV_SEPARATOR = ";";

	// Les noms des différents documents. (avec xls pour forcer le chargemement
	// par un simple lien)
	private static String CSV_ROTATE_WAGON = "taux_rotation_wagons.csv.xls";
	private static String CSV_DELTA_SILLON = "comparaisons_heures_sillons.csv.xls";
	private static String CSV_COUNT_WAGON_RAIL = "nombre_wagons_voie_mouvement.cvs.xls";

	// Propriété pour le répertoire de stockage des csv.
	@Value(value = "${csv.directory.path}")
	private String csvDirectoryPath;

	// Propriété pour le chemin de visualisation des csv.
	@Value(value = "${csv.html.path}")
	private String csvHtmlPath;

	// Les ressources pour les statistiques.
	@Resource(name = "daoReporting")
	IDaoReporting daoReporting;

	// Le format des dates.
	protected static final String FORMAT_DATE = "dd/MM/yyyy";
	// Le format des heures.
	protected static final String FORMAT_TIME = "HH:mm";

	// Les formaters. !! Attention au multiThread !!
	private SimpleDateFormat dateFormatter;
	private SimpleDateFormat timeFormatter;

	/**
	 * Constructeur.
	 */
	ReportingManagerImpl() {

		dateFormatter = new SimpleDateFormat(FORMAT_DATE);
		timeFormatter = new SimpleDateFormat(FORMAT_TIME);
	}

	/**
	 * Création du fichier et obtention d'un BufferedWriter pour écrire dans le
	 * fichier.
	 */
	public BufferedWriter getCsvWriter(String csvFileName, User user)
			throws ApplicationException {

		try {
			File csvFile = new File(
					ManagedBeanUtils.getRealPath(csvDirectoryPath));

			if (!csvFile.exists())
				csvFile.mkdir();

			csvFile = new File(csvFile.getAbsolutePath() + File.separator
					+ user.getId());

			if (!csvFile.exists())
				csvFile.mkdir();

			// On écrase le fichier si existe.
			FileWriter fw = new FileWriter(csvFile.getAbsolutePath()
					+ File.separator + csvFileName, false);

			return new BufferedWriter(fw);

		} catch (IOException e) {
			throw new ApplicationException(e);
		}
	}

	/**
	 * Création de l'ensemble des statistiques pour le hub. On fait tous les
	 * fichiers en un seul coup.
	 */
	@Override
	public List<HubReportData> createCsvReporting(Date date, Date dateEnd,
			User user) throws ApplicationException {

		List<HubReportData> lstReports = new ArrayList<HubReportData>();

		// Temps de rotation.
		try {
			createReportTrafficRotation(date, dateEnd, user);
			lstReports.add(new HubReportData(LabelUtils
					.getLabel("report.rotate.wagon"), csvHtmlPath
					+ user.getId() + PATH_HTML_SEPARATOR + CSV_ROTATE_WAGON));

		} catch (ApplicationException e) {
		}

		// Ecarts sillons.
		try {
			createReportDeltaSillon(date, dateEnd, user);
			lstReports.add(new HubReportData(LabelUtils
					.getLabel("report.delta.sillon"), csvHtmlPath
					+ user.getId() + PATH_HTML_SEPARATOR + CSV_DELTA_SILLON));

		} catch (ApplicationException e) {
		}

		// Nombre de wagons par voie, par mouvement.
		try {
			createReportCountRailWagon(date, dateEnd, user);
			lstReports.add(new HubReportData(LabelUtils
					.getLabel("report.wagon.rail"), csvHtmlPath + user.getId()
					+ PATH_HTML_SEPARATOR + CSV_COUNT_WAGON_RAIL));

		} catch (ApplicationException e) {
		}

		return lstReports;
	}

	/**
	 * Création du fichier de statistiques pour le temps de rotation des wagons
	 * sur le hub (entrées / sorties). Chaque wagon contient deux lignes, la
	 * première pour l'entrée sur le hub, la seconde pour la sortie. Si un wagon
	 * n'est pas encore sorti, alors on saute la ligne.
	 */
	private void createReportTrafficRotation(Date date, Date dateEnd, User user)
			throws ApplicationException {

		BufferedWriter output = null;

		try {
			output = getCsvWriter(CSV_ROTATE_WAGON, user);
			SqlRowSet rowSet = daoReporting
					.reportTrafficRotation(date, dateEnd);

			StringBuffer sb = new StringBuffer();
			int wagonId;

			while (rowSet.next()) {
				sb.setLength(0);

				wagonId = rowSet.getInt(1);
				sb.append(rowSet.getString(2) + CSV_SEPARATOR
						+ rowSet.getString(5) + CSV_SEPARATOR
						+ dateFormatter.format(rowSet.getTimestamp(3))
						+ CSV_SEPARATOR
						+ timeFormatter.format(rowSet.getTimestamp(3))
						+ CSV_SEPARATOR);

				if (!rowSet.isLast())
					rowSet.next();

				if (wagonId == rowSet.getInt(1)) {
					sb.append(dateFormatter.format(rowSet.getTimestamp(3))
							+ CSV_SEPARATOR
							+ timeFormatter.format(rowSet.getTimestamp(3)));
					output.write(sb.toString());
					output.newLine();
				}
			}
			output.flush();
		} catch (DataAccessException e) {
			throw new DaoApplicationException(e);
		} catch (IOException e) {
			throw new ApplicationException(e);
		} catch (Exception e) {
			throw new ApplicationException(e);
		} finally {
			try {
				output.close();
			} catch (IOException e) {
				throw new ApplicationException(e);
			}
		}
	}

	/**
	 * Création du fichier de statistiques pour la mesure des écarts entre les
	 * dates et heures théoriques et réelle pour les sillons.
	 */
	private void createReportDeltaSillon(Date date, Date dateEnd, User user)
			throws ApplicationException {

		BufferedWriter output = null;

		try {
			output = getCsvWriter(CSV_DELTA_SILLON, user);
			SqlRowSet rowSet = daoReporting.reportDeltaSillon(date, dateEnd);
			StringBuffer sb = new StringBuffer();

			while (rowSet.next()) {
				sb.setLength(0);
				sb.append(rowSet.getString(1) + CSV_SEPARATOR);
				if (HubSillonType.ARRIVAL.getValue()
						.equals(rowSet.getString(2))) {
					sb.append(LabelUtils.getLabel("report.arrival"));
				} else {
					sb.append(LabelUtils.getLabel("report.departure"));
				}
				sb.append(CSV_SEPARATOR).append(
						rowSet.getString(3) + CSV_SEPARATOR);
				sb.append(rowSet.getString(8) + CSV_SEPARATOR);
				sb.append(dateFormatter.format(rowSet.getDate(4))
						+ CSV_SEPARATOR);
				sb.append(timeFormatter.format(rowSet.getTime(5))
						+ CSV_SEPARATOR);
				sb.append(dateFormatter.format(rowSet.getDate(6))
						+ CSV_SEPARATOR);
				sb.append(timeFormatter.format(rowSet.getTime(7)));
				output.write(sb.toString());
				output.newLine();
			}
			output.flush();
		} catch (DataAccessException e) {
			throw new DaoApplicationException(e);
		} catch (IOException e) {
			throw new ApplicationException(e);
		} catch (Exception e) {
			throw new ApplicationException(e);
		} finally {
			try {
				output.close();
			} catch (IOException e) {
				throw new ApplicationException(e);
			}
		}
	}

	/**
	 * 
	 */
	private void createReportCountRailWagon(Date date, Date dateEnd, User user)
			throws ApplicationException {

		BufferedWriter output = null;

		try {
			output = getCsvWriter(CSV_COUNT_WAGON_RAIL, user);
			SqlRowSet rowSet = daoReporting.reportCountRailWagon(date, dateEnd);
			StringBuffer sb = new StringBuffer();

			while (rowSet.next()) {
				sb.setLength(0);
				sb.append(dateFormatter.format(rowSet.getDate(2))
						+ CSV_SEPARATOR);
				sb.append(timeFormatter.format(rowSet.getDate(2))
						+ CSV_SEPARATOR);
				sb.append(rowSet.getString(3) + CSV_SEPARATOR);
				sb.append(rowSet.getString(4) + CSV_SEPARATOR);
				sb.append(rowSet.getString(5) + CSV_SEPARATOR);
				sb.append(rowSet.getInt(1));
				output.write(sb.toString());
				output.newLine();
			}
			output.flush();
		} catch (DataAccessException e) {
			throw new DaoApplicationException(e);
		} catch (IOException e) {
			throw new ApplicationException(e);
		} catch (Exception e) {
			throw new ApplicationException(e);
		} finally {
			try {
				output.close();
			} catch (IOException e) {
				throw new ApplicationException(e);
			}
		}
	}
}
